/**
 * Copyright (C) 2010 Daniel <daniel.petisme@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * 
 */
package fr.daniel.todolist.model.services;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.Persistence;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.PersistenceUnit;
import javax.persistence.Query;
import javax.persistence.RollbackException;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.apache.log4j.Logger;
import org.eclipse.persistence.exceptions.DatabaseException;

import fr.daniel.todolist.model.pojo.Priority;

/**
 * @author Daniel
 * 
 */
public class ServicesPriority {

	private static Logger log = Logger.getLogger(ServicesPriority.class);

	/** The unique instance */
	private static ServicesPriority instance;

	// Persitence
	private static final String PERSISTENCE_UNIT_NAME = "todolist";

	@PersistenceUnit(name = "Prioritylist")
	private EntityManagerFactory factory;
	@PersistenceContext
	private EntityManager em;

	/**
	 * Default Constructor
	 */
	private ServicesPriority() {
		factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
		em = factory.createEntityManager();

		if (log.isDebugEnabled()) {
			log.debug("Factory and Entity Manager for priority services");
		}
	}

	/**
	 * Get all the priorities stored
	 * 
	 * @return the list of priorities
	 */
	@SuppressWarnings("unchecked")
	public List<Priority> getAll() {
		Query q = em.createQuery("select p from Priority p");
		return q.getResultList();
	}

	/**
	 * Get a priority stored in database by its ID
	 * 
	 * @param id
	 *            The id of the priority we're looking for
	 * @return A founded priority or null
	 */
	public Priority getById(int id) {
		return getByCriteria("id", id);
	}

	/**
	 * Get a priority stored in database by its label
	 * 
	 * @param label
	 *            The label of the priority we're looking for
	 * @return A founded priority or null
	 */
	public Priority getByLabel(String label) {
		return getByCriteria("label", label);
	}

	/**
	 * Search a Priority for a couple attribute/value
	 * 
	 * @param attribute
	 *            The name of the attribute to compare
	 * @param value
	 *            the value of the attribute
	 * @return the Priority founded or null
	 */
	private Priority getByCriteria(String attribute, Object value) {
		final String parameterName = "param";

		// Use Criteria to make a equivalent of preparedStatement
		CriteriaBuilder queryBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Priority> parameterizedQuery = queryBuilder
				.createQuery(Priority.class);

		// Make the from part of the query
		Root<Priority> PriorityDomain = parameterizedQuery.from(Priority.class);

		// Make the query with the where part
		parameterizedQuery.select(PriorityDomain).where(
				queryBuilder.equal(PriorityDomain.get(attribute),
						queryBuilder.parameter(Object.class, parameterName)));

		// Use the parameter
		Query query = em.createQuery(parameterizedQuery);
		query.setParameter(parameterName, value);

		Priority founded = null;

		// if non-unique value return null
		try {
			if (log.isDebugEnabled()) {
				log.debug("Searching a Priority with attribute : " + attribute
						+ " and value : " + value);
			}
			founded = (Priority) query.getSingleResult();
		} catch (NoResultException e) {
			if (log.isDebugEnabled()) {
				log.debug("No rows founded for atribute : " + attribute
						+ " with value :" + value);
			}
		} catch (NonUniqueResultException e) {
			if (log.isDebugEnabled()) {
				log.debug("Multiple values for atribute : " + attribute
						+ " with value :" + value);
			}
		}

		if (log.isDebugEnabled()) {
			log.debug("One row founded for atribute : " + attribute
					+ " with value :" + value + ", " + founded);
		}
		// Garbage
		query = null;
		PriorityDomain = null;
		parameterizedQuery = null;
		queryBuilder = null;

		return founded;

	}

	/**
	 * Insert a Priority
	 * 
	 * @param toInsert
	 *            have a guess
	 * @return the id of the priority recorded or 0
	 * @throws DatabaseException
	 */
	public int insert(Priority toInsert) throws DatabaseException {
		int newId = 0;
		if (toInsert != null) {
			if (getById(toInsert.getId()) == null) {
			if (getByLabel(toInsert.getLabel()) == null) {				
					EntityTransaction transaction = em.getTransaction();

					// If errors occurred, ROLLBACK
					if (!transaction.isActive()) {

						transaction.begin();

						em.persist(toInsert);
						try {
							transaction.commit();
							newId = toInsert.getId();
						} catch (RollbackException e) {
							log.error("Insert fail, transaction rollbacked");
							// If errors occurred, ROLLBACK
							try {
								em.getTransaction().rollback();
							} catch (IllegalStateException ex) {
								log.error("No active transaction");
								throw ex;
							} catch (PersistenceException ex) {
								log.error("Error in persistance");
								throw ex;
							}

						}

					}
				} else {
					if (log.isDebugEnabled()) {
						log.debug("Priority : " + toInsert
								+ " already exists in the database");
					}
				}
			} else {
				if (log.isDebugEnabled()) {
					log.debug("Priority : " + toInsert
							+ " already exists in the database");
				}
			}
		}

		return newId;
	}

	/**
	 * 
	 * @param toUpdate
	 *            obvious ??
	 * @return sucess
	 * @throws DatabaseException
	 */
	public boolean update(Priority toUpdate) throws DatabaseException {
		boolean sucess = false;

		if (toUpdate != null) {
			EntityTransaction transaction = em.getTransaction();
			Priority toDelete = getById(toUpdate.getId());
			if (toDelete != null) {
				// If errors occurred, ROLLBACK
				if (!transaction.isActive()) {
					transaction.begin();

					toDelete.setLabel(toUpdate.getLabel());
					toDelete.setLevel(toUpdate.getLevel());

					try {
						transaction.commit();
						sucess = true;
					} catch (RollbackException e) {
						log.error("Delete fail, transaction rollbacked");
						// If errors occurred, ROLLBACK
						try {
							em.getTransaction().rollback();
						} catch (IllegalStateException ex) {
							log.error("No active transaction");
							throw ex;
						} catch (PersistenceException ex) {
							log.error("Error in persistance");
							throw ex;
						}
					}
				} else {
					if (log.isDebugEnabled()) {
						log.debug("Priority : " + toDelete
								+ " doesn't exist in database");
					}
				}
			}
		} else {
			if (log.isDebugEnabled()) {
				log.debug("Parameter null");
			}

		}
		return sucess;
	}

	/**
	 * 
	 * @param idPriorityToDelete
	 *            The id of the priority to delete
	 * @return sucess ??
	 * @throws DatabaseException
	 */
	public boolean delete(int idPriorityToDelete) throws DatabaseException {
		boolean sucess = false;

		EntityTransaction transaction = em.getTransaction();
		Priority toDelete = getById(idPriorityToDelete);
		if (toDelete != null) {
			// If errors occurred, ROLLBACK
			if (!transaction.isActive()) {
				transaction.begin();

				try {
					em.remove(toDelete);
				} catch (IllegalArgumentException e) {
					if (log.isDebugEnabled()) {
						log.debug("Bad Entity" + e.getMessage());
					}
				}

				try {
					transaction.commit();
					sucess = true;
				} catch (RollbackException e) {
					log.error("Delete fail, transaction rollbacked");
					// If errors occurred, ROLLBACK
					try {
						em.getTransaction().rollback();
					} catch (IllegalStateException ex) {
						log.error("No active transaction");
						throw ex;
					} catch (PersistenceException ex) {
						log.error("Error in persistance");
						throw ex;
					}
				}
			} else {
				if (log.isDebugEnabled()) {
					log.debug("Priority : " + toDelete
							+ " doesn't exist in database");
				}
			}
		} else {
			if (log.isDebugEnabled()) {
				log.debug("Parameter null");
			}
		}
		return sucess;
	}

	/**
	 *  Access to the unique instance
	 * @return the unique instance
	 */
	public static ServicesPriority getPriorityServer() {
		if (instance == null) {
			instance = new ServicesPriority();
		}

		return instance;
	}

}
